﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;
using System.Timers;

namespace LianLianKan
{
    public partial class Form1 : Form
    {
        private DataStore DS = new DataStore();
        private IntPair First = new IntPair(-1, -1), Second = new IntPair(-1, -1);
        private const int ROWNUM = 12, COLNUM = 16, DMEN = 192;
        private PictureBox[] PB = new PictureBox[DMEN];
        private Panel[] PN = new Panel[DMEN];
        private int[] PBIndex = new int[DMEN];
        private IntPair HintGrid1 = new IntPair(-1, -1), HintGrid2 = new IntPair(-1, -1);
        private int Level = 0;
        private int GameProgress = 0; // maximum = TotalTime
        private object GameProgressObj = new object(); 
        private bool Started = false;
        private bool Paused = false;
        private const int TotalTime = 300000;
        private const int Interval = 100;
        private System.Timers.Timer TimerProgress = new System.Timers.Timer(Interval);
        private Graphics ProgressGrap, PanelContainerGrap;
        private int Score = 0; 
        private const int HintNumConst = 10, LiveNumConst = 9;
        private int HintNum = HintNumConst, LiveNum = LiveNumConst;
        private bool InitialStart = true;
        delegate void SetTextCallback();

        public Form1()
        {
            InitializeComponent();



            PB[0] = pictureBox_0_0; PB[4] = pictureBox_0_4; PB[7] = pictureBox_0_7;
            PB[1] = pictureBox_0_1; PB[5] = pictureBox_0_5; PB[8] = pictureBox_0_8;
            PB[2] = pictureBox_0_2; PB[6] = pictureBox_0_6; PB[9] = pictureBox_0_9;
            PB[3] = pictureBox_0_3; PB[10] = pictureBox_0_10; PB[11] = pictureBox_0_11;
            PB[12] = pictureBox_0_12; PB[13] = pictureBox_0_13; PB[14] = pictureBox_0_14;
            PB[15] = pictureBox_0_15;

            PB[16] = pictureBox_1_0;
            PB[17] = pictureBox_1_1; PB[70] = pictureBox_4_6; PB[123] = pictureBox_7_11;
            PB[18] = pictureBox_1_2; PB[71] = pictureBox_4_7; PB[124] = pictureBox_7_12;
            PB[19] = pictureBox_1_3; PB[72] = pictureBox_4_8; PB[125] = pictureBox_7_13;
            PB[20] = pictureBox_1_4; PB[73] = pictureBox_4_9; PB[126] = pictureBox_7_14;
                                                                PB[127] = pictureBox_7_15;
                                                                PB[128] = pictureBox_8_0;
            PB[21] = pictureBox_1_5; PB[74] = pictureBox_4_10; PB[129] = pictureBox_8_1;
            PB[22] = pictureBox_1_6; PB[75] = pictureBox_4_11; PB[130] = pictureBox_8_2;
            PB[23] = pictureBox_1_7; PB[76] = pictureBox_4_12; PB[131] = pictureBox_8_3;
            PB[24] = pictureBox_1_8; PB[77] = pictureBox_4_13; PB[132] = pictureBox_8_4;
            PB[25] = pictureBox_1_9; PB[78] = pictureBox_4_14; PB[133] = pictureBox_8_5;
                                    PB[79] = pictureBox_4_15;
                                    PB[80] = pictureBox_5_0;
            PB[26] = pictureBox_1_10; PB[81] = pictureBox_5_1; PB[134] = pictureBox_8_6;
            PB[27] = pictureBox_1_11; PB[82] = pictureBox_5_2; PB[135] = pictureBox_8_7;
            PB[28] = pictureBox_1_12; PB[83] = pictureBox_5_3; PB[136] = pictureBox_8_8;
            PB[29] = pictureBox_1_13; PB[84] = pictureBox_5_4; PB[137] = pictureBox_8_9;
            PB[30] = pictureBox_1_14; PB[85] = pictureBox_5_5; PB[138] = pictureBox_8_10;
            PB[31] = pictureBox_1_15;
            PB[32] = pictureBox_2_0;
            PB[33] = pictureBox_2_1; PB[86] = pictureBox_5_6; PB[139] = pictureBox_8_11;
            PB[34] = pictureBox_2_2; PB[87] = pictureBox_5_7; PB[140] = pictureBox_8_12;
            PB[35] = pictureBox_2_3; PB[88] = pictureBox_5_8; PB[141] = pictureBox_8_13;
            PB[36] = pictureBox_2_4; PB[89] = pictureBox_5_9; PB[142] = pictureBox_8_14;
                                                                PB[143] = pictureBox_8_15;
                                                                PB[144] = pictureBox_9_0;
            PB[37] = pictureBox_2_5; PB[90] = pictureBox_5_10; PB[145] = pictureBox_9_1;
            PB[38] = pictureBox_2_6; PB[91] = pictureBox_5_11; PB[146] = pictureBox_9_2;
            PB[39] = pictureBox_2_7; PB[92] = pictureBox_5_12; PB[147] = pictureBox_9_3;
            PB[40] = pictureBox_2_8; PB[93] = pictureBox_5_13; PB[148] = pictureBox_9_4;
            PB[41] = pictureBox_2_9; PB[94] = pictureBox_5_14; PB[149] = pictureBox_9_5;
                                        PB[95] = pictureBox_5_15;
                                        PB[96] = pictureBox_6_0;
            PB[42] = pictureBox_2_10; PB[97] = pictureBox_6_1; PB[150] = pictureBox_9_6;
            PB[43] = pictureBox_2_11; PB[98] = pictureBox_6_2; PB[151] = pictureBox_9_7;
            PB[44] = pictureBox_2_12; PB[99] = pictureBox_6_3; PB[152] = pictureBox_9_8;
            PB[45] = pictureBox_2_13; PB[100] = pictureBox_6_4; PB[153] = pictureBox_9_9;
            PB[46] = pictureBox_2_14; PB[101] = pictureBox_6_5; PB[154] = pictureBox_9_10;
            PB[47] = pictureBox_2_15;
            PB[48] = pictureBox_3_0;
            PB[49] = pictureBox_3_1; PB[102] = pictureBox_6_6; PB[155] = pictureBox_9_11;
            PB[50] = pictureBox_3_2; PB[103] = pictureBox_6_7; PB[156] = pictureBox_9_12;
            PB[51] = pictureBox_3_3; PB[104] = pictureBox_6_8; PB[157] = pictureBox_9_13;
            PB[52] = pictureBox_3_4; PB[105] = pictureBox_6_9; PB[158] = pictureBox_9_14;
                                                                PB[159] = pictureBox_9_15;
                                                                PB[160] = pictureBox_10_0;
            PB[53] = pictureBox_3_5; PB[106] = pictureBox_6_10; PB[161] = pictureBox_10_1;
            PB[54] = pictureBox_3_6; PB[107] = pictureBox_6_11; PB[162] = pictureBox_10_2;
            PB[55] = pictureBox_3_7; PB[108] = pictureBox_6_12; PB[163] = pictureBox_10_3;
            PB[56] = pictureBox_3_8; PB[109] = pictureBox_6_13; PB[164] = pictureBox_10_4;
            PB[57] = pictureBox_3_9; PB[110] = pictureBox_6_14; PB[165] = pictureBox_10_5;
                                        PB[111] = pictureBox_6_15;
                                        PB[112] = pictureBox_7_0;
            PB[58] = pictureBox_3_10; PB[113] = pictureBox_7_1; PB[166] = pictureBox_10_6;
            PB[59] = pictureBox_3_11; PB[114] = pictureBox_7_2; PB[167] = pictureBox_10_7;
            PB[60] = pictureBox_3_12; PB[115] = pictureBox_7_3; PB[168] = pictureBox_10_8;
            PB[61] = pictureBox_3_13; PB[116] = pictureBox_7_4; PB[169] = pictureBox_10_9;
            PB[62] = pictureBox_3_14; PB[117] = pictureBox_7_5; PB[170] = pictureBox_10_10;
            PB[63] = pictureBox_3_15;
            PB[64] = pictureBox_4_0;
            PB[65] = pictureBox_4_1; PB[118] = pictureBox_7_6; PB[171] = pictureBox_10_11;
            PB[66] = pictureBox_4_2; PB[119] = pictureBox_7_7; PB[172] = pictureBox_10_12;
            PB[67] = pictureBox_4_3; PB[120] = pictureBox_7_8; PB[173] = pictureBox_10_13;
            PB[68] = pictureBox_4_4; PB[121] = pictureBox_7_9; PB[174] = pictureBox_10_14;
            PB[69] = pictureBox_4_5; PB[122] = pictureBox_7_10; PB[175] = pictureBox_10_15;


            PB[176] = pictureBox_11_0; PB[180] = pictureBox_11_4; PB[183] = pictureBox_11_7;
            PB[177] = pictureBox_11_1; PB[181] = pictureBox_11_5; PB[184] = pictureBox_11_8;
            PB[178] = pictureBox_11_2; PB[182] = pictureBox_11_6; PB[185] = pictureBox_11_9;
            PB[179] = pictureBox_11_3; PB[186] = pictureBox_11_10; PB[187] = pictureBox_11_11;
            PB[188] = pictureBox_11_12; PB[189] = pictureBox_11_13; PB[190] = pictureBox_11_14;
            PB[191] = pictureBox_11_15;


            PN[0] = panel_0_0; PN[4] = panel_0_4; PN[7] = panel_0_7;
            PN[1] = panel_0_1; PN[5] = panel_0_5; PN[8] = panel_0_8;
            PN[2] = panel_0_2; PN[6] = panel_0_6; PN[9] = panel_0_9;
            PN[3] = panel_0_3; PN[10] = panel_0_10; PN[11] = panel_0_11;
            PN[12] = panel_0_12; PN[13] = panel_0_13; PN[14] = panel_0_14;
            PN[15] = panel_0_15;

            PN[16] = panel_1_0;
            PN[17] = panel_1_1; PN[70] = panel_4_6; PN[123] = panel_7_11;
            PN[18] = panel_1_2; PN[71] = panel_4_7; PN[124] = panel_7_12;
            PN[19] = panel_1_3; PN[72] = panel_4_8; PN[125] = panel_7_13;
            PN[20] = panel_1_4; PN[73] = panel_4_9; PN[126] = panel_7_14;
            PN[127] = panel_7_15;
            PN[128] = panel_8_0;
            PN[21] = panel_1_5; PN[74] = panel_4_10; PN[129] = panel_8_1;
            PN[22] = panel_1_6; PN[75] = panel_4_11; PN[130] = panel_8_2;
            PN[23] = panel_1_7; PN[76] = panel_4_12; PN[131] = panel_8_3;
            PN[24] = panel_1_8; PN[77] = panel_4_13; PN[132] = panel_8_4;
            PN[25] = panel_1_9; PN[78] = panel_4_14; PN[133] = panel_8_5;
            PN[79] = panel_4_15;
            PN[80] = panel_5_0;
            PN[26] = panel_1_10; PN[81] = panel_5_1; PN[134] = panel_8_6;
            PN[27] = panel_1_11; PN[82] = panel_5_2; PN[135] = panel_8_7;
            PN[28] = panel_1_12; PN[83] = panel_5_3; PN[136] = panel_8_8;
            PN[29] = panel_1_13; PN[84] = panel_5_4; PN[137] = panel_8_9;
            PN[30] = panel_1_14; PN[85] = panel_5_5; PN[138] = panel_8_10;
            PN[31] = panel_1_15;
            PN[32] = panel_2_0;
            PN[33] = panel_2_1; PN[86] = panel_5_6; PN[139] = panel_8_11;
            PN[34] = panel_2_2; PN[87] = panel_5_7; PN[140] = panel_8_12;
            PN[35] = panel_2_3; PN[88] = panel_5_8; PN[141] = panel_8_13;
            PN[36] = panel_2_4; PN[89] = panel_5_9; PN[142] = panel_8_14;
            PN[143] = panel_8_15;
            PN[144] = panel_9_0;
            PN[37] = panel_2_5; PN[90] = panel_5_10; PN[145] = panel_9_1;
            PN[38] = panel_2_6; PN[91] = panel_5_11; PN[146] = panel_9_2;
            PN[39] = panel_2_7; PN[92] = panel_5_12; PN[147] = panel_9_3;
            PN[40] = panel_2_8; PN[93] = panel_5_13; PN[148] = panel_9_4;
            PN[41] = panel_2_9; PN[94] = panel_5_14; PN[149] = panel_9_5;
            PN[95] = panel_5_15;
            PN[96] = panel_6_0;
            PN[42] = panel_2_10; PN[97] = panel_6_1; PN[150] = panel_9_6;
            PN[43] = panel_2_11; PN[98] = panel_6_2; PN[151] = panel_9_7;
            PN[44] = panel_2_12; PN[99] = panel_6_3; PN[152] = panel_9_8;
            PN[45] = panel_2_13; PN[100] = panel_6_4; PN[153] = panel_9_9;
            PN[46] = panel_2_14; PN[101] = panel_6_5; PN[154] = panel_9_10;
            PN[47] = panel_2_15;
            PN[48] = panel_3_0;
            PN[49] = panel_3_1; PN[102] = panel_6_6; PN[155] = panel_9_11;
            PN[50] = panel_3_2; PN[103] = panel_6_7; PN[156] = panel_9_12;
            PN[51] = panel_3_3; PN[104] = panel_6_8; PN[157] = panel_9_13;
            PN[52] = panel_3_4; PN[105] = panel_6_9; PN[158] = panel_9_14;
            PN[159] = panel_9_15;
            PN[160] = panel_10_0;
            PN[53] = panel_3_5; PN[106] = panel_6_10; PN[161] = panel_10_1;
            PN[54] = panel_3_6; PN[107] = panel_6_11; PN[162] = panel_10_2;
            PN[55] = panel_3_7; PN[108] = panel_6_12; PN[163] = panel_10_3;
            PN[56] = panel_3_8; PN[109] = panel_6_13; PN[164] = panel_10_4;
            PN[57] = panel_3_9; PN[110] = panel_6_14; PN[165] = panel_10_5;
            PN[111] = panel_6_15;
            PN[112] = panel_7_0;
            PN[58] = panel_3_10; PN[113] = panel_7_1; PN[166] = panel_10_6;
            PN[59] = panel_3_11; PN[114] = panel_7_2; PN[167] = panel_10_7;
            PN[60] = panel_3_12; PN[115] = panel_7_3; PN[168] = panel_10_8;
            PN[61] = panel_3_13; PN[116] = panel_7_4; PN[169] = panel_10_9;
            PN[62] = panel_3_14; PN[117] = panel_7_5; PN[170] = panel_10_10;
            PN[63] = panel_3_15;
            PN[64] = panel_4_0;
            PN[65] = panel_4_1; PN[118] = panel_7_6; PN[171] = panel_10_11;
            PN[66] = panel_4_2; PN[119] = panel_7_7; PN[172] = panel_10_12;
            PN[67] = panel_4_3; PN[120] = panel_7_8; PN[173] = panel_10_13;
            PN[68] = panel_4_4; PN[121] = panel_7_9; PN[174] = panel_10_14;
            PN[69] = panel_4_5; PN[122] = panel_7_10; PN[175] = panel_10_15;


            PN[176] = panel_11_0; PN[180] = panel_11_4; PN[183] = panel_11_7;
            PN[177] = panel_11_1; PN[181] = panel_11_5; PN[184] = panel_11_8;
            PN[178] = panel_11_2; PN[182] = panel_11_6; PN[185] = panel_11_9;
            PN[179] = panel_11_3; PN[186] = panel_11_10; PN[187] = panel_11_11;
            PN[188] = panel_11_12; PN[189] = panel_11_13; PN[190] = panel_11_14;
            PN[191] = panel_11_15;

            ProgressGrap = panelProgressBar.CreateGraphics();
            PanelContainerGrap = panelContainer.CreateGraphics();
            TimerProgress.Elapsed += new ElapsedEventHandler(OnTimedEvent);

            SetStyle(ControlStyles.UserPaint, true);
            SetStyle(ControlStyles.AllPaintingInWmPaint, true);
            SetStyle(ControlStyles.DoubleBuffer, true);
            this.UpdateStyles();
        }

        private void buttonHint_Click(object sender, EventArgs e)
        {
            if (!Started) return;
            if (HintNum > 0)
            {
                HintNum--;
                UpdateHint(HintNum); 
            }
            else
            {
                return;
            }
            IntPair grid1 = new IntPair(0, 0);
            IntPair grid2 = new IntPair(0, 0);
            if (DS.FindRemovableGrids(grid1, grid2))
            {
                // HintGrid1.SetValue(grid1);
                // HintGrid2.SetValue(grid2);
                HintGrid1.SetValue(DS.RGrid1);
                HintGrid2.SetValue(DS.RGrid2);
                PB[HintGrid1.i1 * COLNUM + HintGrid1.i2].Refresh();
                PB[HintGrid2.i1 * COLNUM + HintGrid2.i2].Refresh();
            }
        }

        private void HideGrids() {
            for (int row = 1; row<ROWNUM-1; row++) {
                for (int col=1; col<COLNUM-1; col++) {
                    PN[row * COLNUM + col].Visible = false;
                    // PB[row * COLNUM + col].Hide();
                    // PB[row * COLNUM + col].BorderStyle = BorderStyle.None;
                }
            }
        }

        private void ShowGrids()
        {
            for (int row = 1; row < ROWNUM-1; row++)
            {
                for (int col = 1; col < COLNUM-1; col++)
                {
                    if (DS.GetData(row, col) != -1)
                    {
                        PN[row * COLNUM + col].Visible = true;
                        // PB[row * COLNUM + col].Show();
                    }
                }
            }
        }

        private void InitGridsBorder()
        {
            for (int row = 1; row < ROWNUM-1; row++)
            {
                for (int col = 1; col < COLNUM-1; col++)
                {
                    PB[row * COLNUM + col].BorderStyle = BorderStyle.FixedSingle;
                }
            }
        }

        private void pictureBox_Paint(object sender, PaintEventArgs e)
        {
            IntPair pos = new IntPair(-1, -1);
            ParsePosition(((PictureBox)sender).Name, pos);
            if ((pos.i1 == HintGrid1.i1 && pos.i2 == HintGrid1.i2) ||
                (pos.i1 == HintGrid2.i1 && pos.i2 == HintGrid2.i2))
            {
                Pen redPen = new Pen(Color.Red, 5);
                e.Graphics.DrawLine(redPen, new Point(0, 0), new Point(0, 43));
                e.Graphics.DrawLine(redPen, new Point(0, 0), new Point(43, 0));
                e.Graphics.DrawLine(redPen, new Point(0, 43), new Point(43, 43));
                e.Graphics.DrawLine(redPen, new Point(43, 0), new Point(43, 43));
            }
        }

        private void ParsePosition(string Name, IntPair Position)
        {
            int i1 = Name.IndexOf('_');
            int i2 = Name.IndexOf('_', i1 + 1);
            string strRow = Name.Substring(i1 + 1, i2 - i1 - 1);
            string strCol = Name.Substring(i2 + 1);
            Position.i1 = Convert.ToInt32(strRow);
            Position.i2 = Convert.ToInt32(strCol);
        }

        private void InitGrids()
        {
            ShowGrids();
            InitGridsBorder(); 
        }

        private void buttonNext_Click(object sender, EventArgs e)
        {
            for (int i = 0; i < DMEN; i++)
            {
                PBIndex[i] = -1; 
            }
            Level++;
            // Level = 9; 
            if (Level > 9) Level = 1; 

            switch (Level)
            {
                case 1:
                    labelGame.Text = "Game One - Beginner";
                    this.panelContainer.BackgroundImage = global::LianLianKan.Properties.Resources._11;
                    break;
                case 2:
                    labelGame.Text = "Game Two - Advanced";
                    this.panelContainer.BackgroundImage = global::LianLianKan.Properties.Resources._22;
                    break;
                case 3:
                    labelGame.Text = "Game Three - Up & Down";
                    this.panelContainer.BackgroundImage = global::LianLianKan.Properties.Resources._33;
                    break;
                case 4:
                    labelGame.Text = "Game Four - Left & Right";
                    this.panelContainer.BackgroundImage = global::LianLianKan.Properties.Resources._44;
                    break;
                case 5:
                    labelGame.Text = "Game Five - To Left";
                    this.panelContainer.BackgroundImage = global::LianLianKan.Properties.Resources._55;
                    break;
                case 6:
                    labelGame.Text = "Game Six - To Right";
                    this.panelContainer.BackgroundImage = global::LianLianKan.Properties.Resources._66;
                    break;
                case 7:
                    labelGame.Text = "Game Seven - To Down";
                    this.panelContainer.BackgroundImage = global::LianLianKan.Properties.Resources._77;
                    break;
                case 8:
                    labelGame.Text = "Game Eight - To Top";
                    this.panelContainer.BackgroundImage = global::LianLianKan.Properties.Resources._88;
                    break;
                case 9:
                    labelGame.Text = "Game Night - To Center";
                    this.panelContainer.BackgroundImage = global::LianLianKan.Properties.Resources._99;
                    break;
            }
            DS.GenerateProblem(Level, imageList.Images.Count);
            InitGrids();
            for (int i = 0; i < DMEN; i++)
            {
                string name = PB[i].Name;
                IntPair IP = new IntPair(-1, -1);
                ParsePosition(name, IP);
                int row = IP.i1;
                int col = IP.i2;
                int imgIndex = DS.GetData(row, col);
                if (imgIndex != -1)
                {
                    PB[i].Image = imageList.Images[imgIndex];
                    PBIndex[i] = imgIndex;
                }
            }
            panelContainer.Focus();
            panelContainer.Refresh();
            buttonNext.Enabled = false;

            List<int> temp = new List<int>();
            for (int i = 0; i < imageList.Images.Count; i++)
            {
                temp.Add(0);
            }
            for (int i = 0; i < DMEN; i++)
            {
                if (PBIndex[i] != -1)
                    temp[PBIndex[i]]++;
            }
            HintNum += 2; 
            buttonHint.Text = "Hint " + HintNum.ToString();

            LiveNum += 1;
            // buttonLive.Text = "Live " + LiveNum.ToString();
            UpdateLive(LiveNum);

            TimerProgress.Interval = Interval;
            GameProgress = TotalTime;
            if (InitialStart)
            {
                InitialStart = false;
                Score = 0; 
                UpdateScore(0);
            }
            panelBackGround.Visible = false;
            TimerProgress.Start();
            Started = true;
        }

        private void OnTimedEvent(object source, ElapsedEventArgs e)
        {
            if (!Started) return;

            lock (GameProgressObj)
            {
                GameProgress = GameProgress - Interval;
                if (GameProgress < 0)
                {
                    GameProgress = 0;
                }
                lock (panelProgressBar)
                {
                    Brush blueBrush = new SolidBrush(Color.Blue);
                    int barLength = panelProgressBar.Size.Width * GameProgress / TotalTime;
                    ProgressGrap.Clear(Color.White);
                    ProgressGrap.FillRectangle(blueBrush, new Rectangle(new Point(0, 0),
                        new Size(barLength, panelProgressBar.Size.Height)));
                }
                if (GameProgress == 0)
                {
                    GameOver();
                }
            }

        }

        private void SetButtonText()
        {
            if (this.buttonHint.InvokeRequired)
            {
                SetTextCallback d = new SetTextCallback(SetButtonText);
                this.Invoke(d, new object[] { });
            }
            else
            {
                buttonHint.Text = "Hint";
            }

            if (this.buttonLive.InvokeRequired)
            {
                SetTextCallback d = new SetTextCallback(SetButtonText);
                this.Invoke(d, new object[] { });
            }
            else
            {
                buttonLive.Text = "Live";
            }

            if (this.panelBackGround.InvokeRequired)
            {
                SetTextCallback d = new SetTextCallback(SetButtonText);
                this.Invoke(d, new object[] { });
            }
            else
            {
                panelBackGround.Visible = true;
            }

            if (this.buttonNext.InvokeRequired)
            {
                SetTextCallback d = new SetTextCallback(SetButtonText);
                this.Invoke(d, new object[] { });
            }
            else
            {
                buttonNext.Enabled = true;
            }
        }

        private void GameOver()
        {
            Started = false;
            DS.InitData();
            
            // buttonHint.Text = "Hint";
            // buttonLive.Text = "Live";
            SetButtonText();

            Level = 0;
            // HideGrids();
            
            // panelBackGround.Visible = true;
            // buttonNext.Enabled = true;
            InitialStart = true;
            HintNum = HintNumConst;
            LiveNum = LiveNumConst;
            // UpdateImage();
        }

        private bool isSelected(IntPair intPair)
        {
            if ((intPair.i1 == -1) || (intPair.i2 == -1))
                return false;
            else
                return true;
        }

        private bool IsConnected(IntPair First, IntPair Second)
        {
            GameData GD = new GameData(DS);
            // check whether the images are adjacient
            // if (First
            return true;
        }

        private void DrawDirection(int direction, Graphics g, int row, int col)
        {
            int rowoffset = 45 * row;
            int coloffset = 45 * col; 

            Pen blue = new Pen(Color.DarkBlue, 5);

            if (direction == 0)
            {
                g.DrawLine(blue, new Point(26 + coloffset, 23 + rowoffset), new Point(0 + coloffset, 23 + rowoffset));
            }
            else if (direction == 1)
            {
                g.DrawLine(blue, new Point(20 + coloffset, 23 + rowoffset), new Point(45 + coloffset, 23 + rowoffset));
            }
            else if (direction == 2)
            {
                g.DrawLine(blue, new Point(23 + coloffset, 26 + rowoffset), new Point(23 + coloffset, 0 + rowoffset));
            }
            else
            {
                g.DrawLine(blue, new Point(23 + coloffset, 20 + rowoffset), new Point(23 + coloffset, 45 + rowoffset));
            }
        }

        private void UpdateImage()
        {
            for (int row = 1; row <= ROWNUM - 2; row++)
            {
                for (int col = 1; col <= COLNUM - 2; col++)
                {
                    if (PBIndex[row * COLNUM + col] != DS.GetData(row, col))
                    {
                        PBIndex[row * COLNUM + col] = DS.GetData(row, col);
                        if (DS.GetData(row, col) == -1)
                        {
                            PB[row * COLNUM + col].Image = null;
                            PN[row * COLNUM + col].Visible = false;
                        }
                        else
                        {
                            PB[row * COLNUM + col].Image = imageList.Images[DS.GetData(row, col)];
                            PN[row * COLNUM + col].Visible = true;
                        }
                        PB[row * COLNUM + col].Refresh();
                    }
                }
            }
        }

        private void UpdateScore(int scoreDelta)
        {
            Score += scoreDelta;
            labelScore.Text = Score.ToString();
        }

        private void UpdateHint(int hintNum)
        {
            HintNum = hintNum;
            buttonHint.Text = "Hint " + HintNum.ToString(); 
        }

        private void UpdateLive(int liveNum)
        {
            LiveNum = liveNum;
            buttonLive.Text = "Live " + liveNum.ToString();
        }

        private void UpdateGridBorder(IntPair grid)
        {
            for (int col = 1; col < COLNUM; col++)
            {
                if (DS.GetData(grid.i1, col) == -1)
                    PB[grid.i1 * COLNUM + col].BorderStyle = BorderStyle.None;
            }
            for (int row = 1; row < ROWNUM; row++)
            {
                if (DS.GetData(row, grid.i2) == -1)
                    PB[row * COLNUM + grid.i2].BorderStyle = BorderStyle.None;
            }
        }

        private void pictureBox_Click(object sender, EventArgs e)
        {
            if (!Started) 
                return;

            IntPair IP = new IntPair(-1, -1);
            ParsePosition(((PictureBox)sender).Name, IP);
            if (DS.GetData(IP.i1, IP.i2) == -1)
            {
                return;
            }

            // ((PictureBox)sender).BorderStyle = BorderStyle.FixedSingle;
            ((PictureBox)sender).BorderStyle = BorderStyle.Fixed3D;
            ((PictureBox)sender).Refresh();
            if (!isSelected(First))
            {
                
                First.SetValue(IP);

                return;
            }
            else
            {
                if (DS.GetData(First.i1, First.i2) != DS.GetData(IP.i1, IP.i2))
                {
                    PB[First.i1 * COLNUM + First.i2].BorderStyle = BorderStyle.FixedSingle;
                    First.SetValue(IP);
                }
                else
                {
                    // check whether they are connected
                    Second.SetValue(IP);

                    List<IntPair> grids = new List<IntPair>();
                    List<int> directs = new List<int>();

                    if (DS.IsRemovableGrids(First, Second, grids, directs))
                    {
                        lock (GameProgressObj)
                        {
                            GameProgress += 2000;
                            if (GameProgress >= TotalTime)
                                GameProgress = TotalTime;

                            // draw lines
                            /*
                            for (int i = 0; i < grids.Count; i++)
                            {
                                richTextBox1.AppendText(grids[i].i1.ToString() + "-" + grids[i].i2.ToString() + ", ");
                            }
                            richTextBox1.AppendText("\n");
                            for (int i = 0; i < directs.Count; i++)
                            {
                                richTextBox1.AppendText(directs[i].ToString() + ", ");
                            }
                            richTextBox1.AppendText("\n");
                            */

                            PB[First.i1 * COLNUM + First.i2].BorderStyle = BorderStyle.FixedSingle;
                            PB[First.i1 * COLNUM + First.i2].Refresh();
                            PB[Second.i1 * COLNUM + Second.i2].BorderStyle = BorderStyle.FixedSingle;
                            PB[Second.i1 * COLNUM + Second.i2].Refresh();
                            // panelContainer.Refresh(); 
                            // Bitmap b = new Bitmap(this.Width, this.Height);
                            // Graphics dc = Graphics.FromImage((System.Drawing.Image)b);

                            bool drawOnPanel = false;
                            for (int i = 0; i < grids.Count - 1; i++)
                            {
                                if ((grids[i].i1 == 0) || (grids[i].i1 == 11) ||
                                    (grids[i].i2 == 0) || (grids[i].i2 == 15) || (PB[grids[i].i1 * COLNUM + grids[i].i2].Image != null))
                                {
                                    DrawDirection(directs[i], PB[grids[i].i1 * COLNUM + grids[i].i2].CreateGraphics(), 0, 0);
                                }
                                else
                                {
                                    DrawDirection(directs[i], PanelContainerGrap, grids[i].i1, grids[i].i2);
                                    drawOnPanel = true;
                                }
                                int d = 0;
                                if (directs[i] == 0) d = 1;
                                else if (directs[i] == 1) d = 0;
                                else if (directs[i] == 2) d = 3;
                                else d = 2;

                                if ((grids[i + 1].i1 == 0) || (grids[i + 1].i1 == 11) ||
                                    (grids[i + 1].i2 == 0) || (grids[i + 1].i2 == 15) || (PB[grids[i + 1].i1 * COLNUM + grids[i + 1].i2].Image != null))
                                {
                                    DrawDirection(d, PB[grids[i + 1].i1 * COLNUM + grids[i + 1].i2].CreateGraphics(), 0, 0);
                                }
                                else
                                {
                                    DrawDirection(d, PanelContainerGrap, grids[i + 1].i1, grids[i + 1].i2);
                                    drawOnPanel = true;
                                }
                            }

                            Thread.Sleep(300);
                            // PB[First.i1 * COLNUM + First.i2].BorderStyle = BorderStyle.None;
                            // PB[Second.i1 * COLNUM + Second.i2].BorderStyle = BorderStyle.None;
                            if (drawOnPanel)
                            {
                                /*
                                PanelContainerGrap.DrawImage(b, 0, 0);
                                dc.Dispose();
                                */
                                // panelContainer.ClientSize = new Size(
                                panelContainer.Refresh();
                            }
                            DS.SetData(First.i1, First.i2, -1);
                            DS.SetData(Second.i1, Second.i2, -1);

                            // PB[First.i1 * COLNUM + First.i2].Image = null;
                            // PB[Second.i1 * COLNUM + Second.i2].Image = null;
                            IntPair hg1 = new IntPair(HintGrid1.i1, HintGrid1.i2), hg2 = new IntPair(HintGrid2.i1, HintGrid2.i2);
                            HintGrid1.SetValue(-1, -1);
                            HintGrid2.SetValue(-1, -1);
                            if (hg1.i1 != -1 && hg1.i2 != -1)
                                PB[hg1.i1 * COLNUM + hg1.i2].Refresh();
                            if (hg2.i1 != -1 && hg2.i2 != -1)
                                PB[hg2.i1 * COLNUM + hg2.i2].Refresh();

                            UpdateScore(10);

                            for (int i = 0; i < grids.Count; i++)
                            {
                                PB[grids[i].i1 * COLNUM + grids[i].i2].Refresh();
                            }
                            DS.ShuffleData(Level, First, Second);

                            UpdateGridBorder(First);
                            UpdateGridBorder(Second);

                            UpdateImage();
                            if (DS.IsComplete())
                            {
                                Started = false;
                                buttonNext.Enabled = true;
                                UpdateScore(GameProgress / 1000);
                                TimerProgress.Stop();
                                panelContainer.Refresh();
                                // GameProgress = 0; 
                                return;
                            }

                            IntPair grid1 = new IntPair(0, 0);
                            IntPair grid2 = new IntPair(0, 0);
                            if (!DS.FindRemovableGrids(grid1, grid2))
                            {
                                TimerProgress.Stop();
                                if (LiveNum <= 0)
                                {
                                    GameOver();
                                    return;
                                }
                                LiveNum--;
                                UpdateLive(LiveNum);

                                MessageBox.Show("No Grid Removable, Shuffle Them Again");
                                do
                                {
                                    DS.ShuffleData(0, grid1, grid2); // grid1 is not used for action '0'

                                } while (!DS.FindRemovableGrids(grid1, grid2));
                                UpdateImage();
                                TimerProgress.Start();
                            }

                            First.SetValue(-1, -1);
                            Second.SetValue(-1, -1);
                        } // lock (GameProgressObj)
                    }
                    else
                    {
                        PB[First.i1 * COLNUM + First.i2].BorderStyle = BorderStyle.FixedSingle;
                        First.SetValue(Second);
                        Second.SetValue(-1, -1);
                    }
                }
            }
        }

        private void Form1_Load(object sender, EventArgs e)
        {

        }

        private void buttonPause_Click(object sender, EventArgs e)
        {
            if (!Started) return;
            if (!Paused)
            {
                Paused = true;
                buttonPause.Text = "Resume";
                TimerProgress.Stop();
                // HideGrids();
                panelBackGround.Visible = true;
            }
            else
            {
                Paused = false;
                buttonPause.Text = "Pause";
                // ShowGrids();
                panelBackGround.Visible = false;
                TimerProgress.Start();
            }
        }

        private void panelProgressBar_Paint(object sender, PaintEventArgs e)
        {
            lock (panelProgressBar)
            {
                Brush blueBrush = new SolidBrush(Color.Blue);
                int barLength = panelProgressBar.Size.Width * GameProgress / TotalTime;
                ProgressGrap.Clear(Color.White);
                ProgressGrap.FillRectangle(blueBrush, new Rectangle(new Point(0, 0),
                    new Size(barLength, panelProgressBar.Size.Height)));
            }
        }

        private void panelContainer_Paint(object sender, PaintEventArgs e)
        {
            base.OnPaint(e);
            Graphics g = e.Graphics;

            Bitmap b = new Bitmap(this.Width, this.Height);
            Graphics dc = Graphics.FromImage((System.Drawing.Image)b);

            g.DrawImage(b, 0, 0);
            dc.Dispose();

            if (DS.IsComplete() && !Started)
            {
                Brush blueBrush = Brushes.Blue;
                Font boldTimesFont = new Font("Times New Roman", 30, FontStyle.Bold);

                string l = "";
                int ii = Level % 9;
                switch (ii)
                {
                    case 1: 
                        l = "II";
                        break;
                    case 2:
                        l = "III";
                        break;
                    case 3:
                        l = "IV";
                        break;
                    case 4:
                        l = "V";
                        break;
                    case 5:
                        l = "VI";
                        break;
                    case 6:
                        l = "VII";
                        break;
                    case 7:
                        l = "IIX";
                        break;
                    case 8:
                        l = "IX";
                        break;
                    case 0:
                        l = "I";
                        break;
                }

                g.DrawString("Continue "+l, boldTimesFont, blueBrush, new Point(275, 200));
            }
        }

        private void buttonLive_Click(object sender, EventArgs e)
        {
        }

        private void panelBackGround_Paint(object sender, PaintEventArgs e)
        {
            base.OnPaint(e);
            Graphics g = e.Graphics;
            Brush blueBrush = Brushes.Blue;
            Font boldTimesFont = new Font("Times New Roman", 30, FontStyle.Bold);
            if (!Started)
            {
                g.DrawString("Start New Game", boldTimesFont, blueBrush, new Point(255, 200));
            }
            else if (Paused)
            {
                g.DrawString("Pause...", boldTimesFont, blueBrush, new Point(300, 200));
            }
        }

        private void pictureBox_6_0_Click(object sender, EventArgs e)
        {

        }

        private void pictureBox_10_0_Click(object sender, EventArgs e)
        {

        }

        private void pictureBox_8_15_Click(object sender, EventArgs e)
        {

        }
    }

    class GameData
    {
        public int[,] Data = new int[12, 16];
        public GameData(DataStore a)
        {
            for (int i = 0; i < 12; i++)
            {
                for (int j = 0; j < 16; j++)
                {
                    Data[i, j] = a.GetData(i, j);
                }
            }
        }
    }
    
    class DataStore
    {
        private int[,] Data = new int[12, 16];
        private List<GameInfo> GI = new List<GameInfo>();
        private const int MAXROW = 11, MAXCOL = 15;
        public IntPair RGrid1 = new IntPair(0, 0);
        public IntPair RGrid2 = new IntPair(0, 0);
        private int ToCenterFlag = 0; 

        public void InitData()
        {
            for (int i = 0; i < 12; i++)
            {
                for (int j = 0; j < 16; j++)
                {
                    Data[i, j] = -1;
                }
            }
        }
        public DataStore()
        {
            InitData();

            GameInfo g1 = new GameInfo(1);
            g1.Info.Add(new IntPair(2, 9));
            g1.Info.Add(new IntPair(4, 11));
            g1.Info.Add(new IntPair(6, 4));
            g1.Info.Add(new IntPair(8, 4));
            g1.Info.Add(new IntPair(10, 1));
            g1.Info.Add(new IntPair(12, 1));
            GI.Add(g1);

            GameInfo g2 = new GameInfo(2);
            g2.Info.Add(new IntPair(2, 11));
            g2.Info.Add(new IntPair(4, 13));
            g2.Info.Add(new IntPair(6, 11));
            GI.Add(g2);

            GameInfo g3 = new GameInfo(1);
            g3.Info.Add(new IntPair(6, 9));
            g3.Info.Add(new IntPair(4, 12));
            g3.Info.Add(new IntPair(2, 19));
            GI.Add(g3);

            GameInfo g4 = new GameInfo(1);
            g4.Info.Add(new IntPair(10, 1));
            g4.Info.Add(new IntPair(6, 7));
            g4.Info.Add(new IntPair(4, 13));
            g4.Info.Add(new IntPair(2, 18));
            GI.Add(g4);

            GameInfo g5 = new GameInfo(1);
            g5.Info.Add(new IntPair(2, 16));
            g5.Info.Add(new IntPair(4, 16));
            g5.Info.Add(new IntPair(6, 6));
            g5.Info.Add(new IntPair(8, 1));
            GI.Add(g5);

            GameInfo g6 = new GameInfo(1);
            g6.Info.Add(new IntPair(2, 16));
            g6.Info.Add(new IntPair(4, 16));
            g6.Info.Add(new IntPair(6, 6));
            g6.Info.Add(new IntPair(8, 1));
            GI.Add(g6);

            GameInfo g7 = new GameInfo(1);
            g7.Info.Add(new IntPair(2, 19));
            g7.Info.Add(new IntPair(4, 11));
            g7.Info.Add(new IntPair(6, 7));
            g7.Info.Add(new IntPair(8, 2));
            GI.Add(g7);

            GameInfo g8 = new GameInfo(1);
            g8.Info.Add(new IntPair(2, 19));
            g8.Info.Add(new IntPair(4, 11));
            g8.Info.Add(new IntPair(6, 7));
            g8.Info.Add(new IntPair(8, 2));
            GI.Add(g8);

            GameInfo g9 = new GameInfo(1);
            g9.Info.Add(new IntPair(2, 19));
            g9.Info.Add(new IntPair(4, 14));
            g9.Info.Add(new IntPair(6, 5));
            g9.Info.Add(new IntPair(8, 2));
            GI.Add(g9);
        }

        public int GetData(int row, int col)
        {
            return Data[row, col];
        }
        public void SetData(int row, int col, int value)
        {
            Data[row, col] = value;
        }

        public void GenerateProblem(int Level, int Ceil)
        {
            GameInfo gi = GI[Level - 1];
            int ic = gi.ImageCount();
            List<int> intBuffer = new List<int>();
            for (int i = 0; i < Ceil; i++)
            {
                intBuffer.Add(i);
            }
            Random rand = new Random();
            for (int i = 0; i < Ceil; i++)
            {
                int r = rand.Next(Ceil);
                if (r != i)
                {
                    int t = intBuffer[i];
                    intBuffer[i] = intBuffer[r];
                    intBuffer[r] = t; 
                }
            }
            int ind = 0; 
            for (int i = 0; i < gi.Info.Count; i++)
            {
                for (int j = 0; j < gi.Info[i].i2; j++)
                {
                    for (int k = 0; k < gi.Info[i].i1; k++)
                    {
                        int rr = rand.Next(10);
                        int rc = rand.Next(14);
                        int r = rr;
                        int c = rc;
                        bool done = false;
                        for (; c < 14; c++)
                        {
                            if (Data[r+1, c+1] == -1)
                            {
                                Data[r+1, c+1] = intBuffer[ind];
                                done = true;
                                break;
                            }
                        }
                        if (done) continue;
                        for (r = rr + 1; r < 10; r++)
                        {
                            for (c = 0; c < 14; c++)
                            {
                                if (Data[r+1, c+1] == -1)
                                {
                                    Data[r+1, c+1] = intBuffer[ind];
                                    done = true;
                                    break;
                                }
                            }
                            if (done) break;
                        }
                        if (done) continue;
                        for (r = 0; r < rr; r++)
                        {
                            for (c = 0; c < 14; c++)
                            {
                                if (Data[r+1, c+1] == -1)
                                {
                                    Data[r+1, c+1] = intBuffer[ind];
                                    done = true;
                                    break;
                                }
                            }
                            if (done) break;
                        }
                        if (done) continue;
                        for (c = 0; c < rc; c++)
                        {
                            if (Data[r+1, c+1] == -1)
                            {
                                Data[r+1, c+1] = intBuffer[ind];
                                done = true;
                                break;
                            }
                        }
                    }

                    ind++;
                }
            }
        }
        public bool FindRemovableGrids(IntPair grid1, IntPair grid2)
        {
            // find a new removable grids
            IntPair tgrid1 = new IntPair(0, 0);
            IntPair tgrid2 = new IntPair(0, 0);
            for (int row = 1; row <= 10; row++)
            {
                for (int col = 1; col <= 14; col++)
                {
                    tgrid1.SetValue(new IntPair(row, col));
                    if (FindRemovable(tgrid1, tgrid2))
                    {
                        grid1 = tgrid1;
                        grid2 = tgrid2;
                        RGrid1 = tgrid1;
                        RGrid2 = tgrid2;

                        return true;
                    }
                }
            }
            return false; 
        }
        private void getSearchDirects(IntPair grid1, IntPair grid2, List<int> searchDirects)
        {
            int rowDiff = grid2.i1 - grid1.i1;
            int colDiff = grid2.i2 - grid1.i2;
            if (rowDiff >= 0)
            {
                if (colDiff >= 0)
                {
                    if (rowDiff > colDiff)
                    {
                        searchDirects.Add(3);
                        searchDirects.Add(1);
                        searchDirects.Add(2);
                        searchDirects.Add(0);
                    }
                    else
                    {
                        searchDirects.Add(1);
                        searchDirects.Add(3);
                        searchDirects.Add(0);
                        searchDirects.Add(2);
                    }
                }
                else
                {
                    if (rowDiff > Math.Abs(colDiff))
                    {
                        searchDirects.Add(3);
                        searchDirects.Add(0);
                        searchDirects.Add(2);
                        searchDirects.Add(1);
                    }
                    else
                    {
                        searchDirects.Add(0);
                        searchDirects.Add(3);
                        searchDirects.Add(1);
                        searchDirects.Add(2);
                    }
                }
            }
            else
            {
                if (colDiff >= 0)
                {
                    if (Math.Abs(rowDiff) > colDiff)
                    {
                        searchDirects.Add(2);
                        searchDirects.Add(1);
                        searchDirects.Add(3);
                        searchDirects.Add(0);
                    }
                    else
                    {
                        searchDirects.Add(1);
                        searchDirects.Add(2);
                        searchDirects.Add(0);
                        searchDirects.Add(3);
                    }
                }
                else
                {
                    if (Math.Abs(rowDiff) > Math.Abs(colDiff))
                    {
                        searchDirects.Add(2);
                        searchDirects.Add(0);
                        searchDirects.Add(3);
                        searchDirects.Add(1);
                    }
                    else
                    {
                        searchDirects.Add(0);
                        searchDirects.Add(2);
                        searchDirects.Add(1);
                        searchDirects.Add(3);
                    }
                }
            }
        }

        public bool IsRemovableGrids(IntPair grid1, IntPair grid2, List<IntPair> grids, List<int> directs)
        {
            if ((Data[grid1.i1, grid1.i2] != Data[grid2.i1, grid2.i2]) ||
                (Data[grid1.i1, grid1.i2] == -1))
            {
                return false;
            }
            grids.Add(grid1);
            List<int> searchDirect = new List<int>();
            getSearchDirects(grid1, grid2, searchDirect);
            for (int i = 0; i < searchDirect.Count; i++)
            {
                if (IsRemoivable_Step(searchDirect[i], 2, grid1, grid2, grids, directs))
                {
                    return true;
                }
            }
            return false;
        }
        public void ShuffleData(int action, IntPair grid1, IntPair grid2)
        {
            if (action == 0)
            {
                // shuffle the left data
                List<int> imageIndex = new List<int>();
                List<IntPair> imagePos = new List<IntPair>();
                for (int row = 1; row < MAXROW; row++)
                {
                    for (int col = 1; col < MAXCOL; col++)
                    {
                        if (Data[row, col] != -1)
                        {
                            imageIndex.Add(Data[row, col]);
                            imagePos.Add(new IntPair(row, col));
                        }
                    }
                }
                Random rand = new Random();

                while (imageIndex.Count > 0)
                {
                    int randNum = rand.Next(imageIndex.Count);
                    Data[imagePos[0].i1, imagePos[0].i2] = imageIndex[randNum];
                    imagePos.RemoveAt(0);
                    imageIndex.RemoveAt(randNum);
                }
            }
            else if (action == 3) // seperate up and down
            {
                Data[grid1.i1, grid1.i2] = -1;
                Data[grid2.i1, grid2.i2] = -1; 
                SepUpAndDown(grid1);
                SepUpAndDown(grid2);
            }
            else if (action == 4) // seperate left and right
            {
                Data[grid1.i1, grid1.i2] = -1;
                Data[grid2.i1, grid2.i2] = -1; 
                SepLeftAndRight(grid1);
                SepLeftAndRight(grid2);
            }
            else if (action == 5) // to left
            {
                Data[grid1.i1, grid1.i2] = -1;
                Data[grid2.i1, grid2.i2] = -1; 
                ToLeft(grid1);
                ToLeft(grid2);
            }
            else if (action == 6) // to right
            {
                Data[grid1.i1, grid1.i2] = -1;
                Data[grid2.i1, grid2.i2] = -1; 
                ToRight(grid1);
                ToRight(grid2);
            }
            else if (action == 7) // to down
            {
                Data[grid1.i1, grid1.i2] = -1;
                Data[grid2.i1, grid2.i2] = -1; 
                ToDown(grid1);
                ToDown(grid2);
            }
            else if (action == 8) // to up
            {
                Data[grid1.i1, grid1.i2] = -1;
                Data[grid2.i1, grid2.i2] = -1; 
                ToUp(grid1);
                ToUp(grid2);
            }
            else if (action == 9) // to center
            {
                Data[grid1.i1, grid1.i2] = -1;
                Data[grid2.i1, grid2.i2] = -1;
                ToCenter(grid1);
                ToCenter(grid2);
            }
        }
        private void SepUpAndDown(IntPair grid)
        {
            if (Data[grid.i1, grid.i2] != -1)
                return;

            if (grid.i1 <= 5)
            {
                int row = grid.i1;
                int repRow = grid.i1 + 1;
                for (; repRow <= 5; repRow++)
                {
                    if (Data[repRow, grid.i2] != -1)
                    {
                        Data[row, grid.i2] = Data[repRow, grid.i2];
                        row++;
                    }
                }
                for (; row <= 5; row++)
                {
                    Data[row, grid.i2] = -1;
                }
            }
            else
            {
                int row = grid.i1;
                int repRow = grid.i1 - 1;
                for (; repRow> 5; repRow--)
                {
                    if (Data[repRow, grid.i2] != -1)
                    {
                        Data[row, grid.i2] = Data[repRow, grid.i2];
                        row--;
                    }
                }
                for (; row > 5; row--)
                {
                    Data[row, grid.i2] = -1;
                }
            }
        }
        private void SepLeftAndRight(IntPair grid)
        {
            if (Data[grid.i1, grid.i2] != -1) 
                return; 
            
            if (grid.i2 <= 7)
            {
                int col = grid.i2;
                int repCol = grid.i2 + 1;
                for (; repCol <= 7; repCol++)
                {
                    if (Data[grid.i1, repCol] != -1)
                    {
                        Data[grid.i1, col] = Data[grid.i1, repCol];
                        col++;
                    }
                }
                for (; col <= 7; col++)
                {
                    Data[grid.i1, col] = -1;
                }
            }
            else
            {
                int col = grid.i2;
                int repCol = grid.i2 - 1;
                for (; repCol > 7; repCol--)
                {
                    if (Data[grid.i1, repCol] != -1)
                    {
                        Data[grid.i1, col] = Data[grid.i1, repCol];
                        col--;
                    }
                }
                for (; col > 7; col--)
                {
                    Data[grid.i1, col] = -1;
                }
            }
        }
        private void ToLeft(IntPair grid)
        {
            if (Data[grid.i1, grid.i2] != -1) return; 
            int repCol = grid.i2+1;
            int col = grid.i2;
            while (repCol < MAXCOL)
            {
                if (Data[grid.i1, repCol] != -1) {
                    Data[grid.i1, col] = Data[grid.i1, repCol];
                    Data[grid.i1, repCol] = -1;
                    do
                    {
                        col++;
                    } while (Data[grid.i1, col] != -1);
                }
                repCol++;
            }
        }
        private void ToRight(IntPair grid)
        {
            if (Data[grid.i1, grid.i2] != -1) return; 
            int repCol = grid.i2 - 1;
            int col = grid.i2;
            while (repCol > 0)
            {
                if (Data[grid.i1, repCol] != -1)
                {
                    Data[grid.i1, col] = Data[grid.i1, repCol];
                    Data[grid.i1, repCol] = -1;
                    do
                    {
                        col--;
                    } while (Data[grid.i1, col] != -1);
                }
                repCol--;
            }
        }
        private void ToDown(IntPair grid)
        {
            if (Data[grid.i1, grid.i2] != -1) return; 
            int repRow = grid.i1 - 1;
            int row = grid.i1;
            while (repRow > 0)
            {
                if (Data[repRow, grid.i2] != -1)
                {
                    Data[row, grid.i2] = Data[repRow, grid.i2];
                    Data[repRow, grid.i2] = -1;
                    do
                    {
                        row--;
                    } while (Data[row, grid.i2] != -1);
                    
                }
                repRow--;
            }
        }
        private void ToUp(IntPair grid)
        {
            if (Data[grid.i1, grid.i2] != -1) return; 
            int repRow = grid.i1 + 1;
            int row = grid.i1;
            while (repRow < MAXROW)
            {
                if (Data[repRow, grid.i2] != -1)
                {
                    Data[row, grid.i2] = Data[repRow, grid.i2];
                    Data[repRow, grid.i2] = -1;
                    do
                    {
                        row++;
                    } while (Data[row, grid.i2] != -1);
                 
                }
                repRow++;
            }
        }
        private void ToCenter(IntPair grid)
        {
             
            if ((grid.i1 <= 5) && (grid.i2 <= 7))
            {
                if (ToCenterFlag == 0)
                {
                    ToDown(grid);
                    for (int row = grid.i1; row >= 1; row--)
                    {
                        ToRight(new IntPair(row, grid.i2));
                    }
                    ToCenterFlag = 1;
                }
                else
                {
                    ToRight(grid);
                    for (int col = grid.i2; col >= 1; col--)
                    {
                        ToDown(new IntPair(grid.i1, col));
                    }
                    ToCenterFlag = 0;
                }
            }
            else if ((grid.i1 <= 5) && (grid.i2 >= 8))
            {
                if (ToCenterFlag == 0)
                {
                    ToDown(grid);
                    for (int row = grid.i1; row >= 1; row--)
                    {
                        ToLeft(new IntPair(row, grid.i2));
                    }
                    ToCenterFlag = 1;
                }
                else
                {
                    ToLeft(grid);
                    for (int col = grid.i2; col <= 14; col++)
                    {
                        ToDown(new IntPair(grid.i1, col));
                    }
                    ToCenterFlag = 0;
                }
            }
            else if ((grid.i1 >= 6) && (grid.i2 <= 7))
            {
                if (ToCenterFlag == 0)
                {
                    ToUp(grid);
                    for (int row = grid.i1; row <= 10; row++)
                    {
                        ToRight(new IntPair(row, grid.i2));
                    }
                    ToCenterFlag = 1;
                }
                else
                {
                    ToRight(grid);
                    for (int col = grid.i2; col >= 1; col--)
                    {
                        ToUp(new IntPair(grid.i1, col));
                    }
                    ToCenterFlag = 0;
                }
            }
            else
            {
                if (ToCenterFlag == 0)
                {
                    ToUp(grid);
                    for (int row = grid.i1; row <= 10; row++)
                    {
                        ToLeft(new IntPair(row, grid.i2));
                    }
                    ToCenterFlag = 1;
                }
                else
                {
                    ToLeft(grid);
                    for (int col = grid.i2; col <= 14; col++)
                    {
                        ToUp(new IntPair(grid.i1, col));
                    }
                    ToCenterFlag = 0;
                }
            }
        }
        public bool IsComplete()
        {
            for (int row = 1; row < MAXROW; row++)
            {
                for (int col = 1; col < MAXCOL; col++)
                {
                    if (Data[row, col] != -1)
                        return false;
                }
            }
            return true;
        }

        /*
         * direction: 0 - left, 1 - right, 2 - up, 3 - down
         */
        private bool IsRemoivable_Step(int direction, int turnNum,
            IntPair grid1, IntPair grid2, List<IntPair> grids, List<int> directs)
        {
            int pathIndex = grids.Count;

            if (direction == 0)
            {
                int row = grid1.i1;
                int col = grid1.i2 - 1;
                while ((col >= 0) && (Data[row, col] == -1) && !(row == grid2.i1 && col == grid2.i2))
                {
                    grids.Add(new IntPair(row, col));
                    directs.Add(0);
                    if (turnNum != 0)
                    {
                        if (IsRemoivable_Step(2, turnNum - 1, new IntPair(row, col), grid2, grids, directs))
                        {
                            return true;
                        }
                        else if (IsRemoivable_Step(3, turnNum - 1, new IntPair(row, col), grid2, grids, directs))
                        {
                            return true;
                        }
                    }
                    col--;
                }
                if ((col >= 0) && (row == grid2.i1 && col == grid2.i2))
                {
                    grids.Add(new IntPair(row, col));
                    directs.Add(0);
                    return true;
                }
                else
                {
                    int range = grids.Count - pathIndex;
                    grids.RemoveRange(pathIndex, range);
                    directs.RemoveRange(pathIndex - 1, range);
                    return false;
                }
            }
            else if (direction == 1)
            {
                int row = grid1.i1;
                int col = grid1.i2 + 1;
                while ((col <= MAXCOL) && (Data[row, col] == -1) && !(row == grid2.i1 && col == grid2.i2))
                {
                    grids.Add(new IntPair(row, col));
                    directs.Add(1);
                    if (turnNum != 0)
                    {
                        if (IsRemoivable_Step(2, turnNum - 1, new IntPair(row, col), grid2, grids, directs))
                        {
                            return true;
                        }
                        else if (IsRemoivable_Step(3, turnNum - 1, new IntPair(row, col), grid2, grids, directs))
                        {
                            return true;
                        }
                    }
                    col++;
                }
                if ((col <= MAXCOL) && (row == grid2.i1 && col == grid2.i2))
                {
                    grids.Add(new IntPair(row, col));
                    directs.Add(1);
                    return true;
                }
                else
                {
                    int range = grids.Count - pathIndex;
                    grids.RemoveRange(pathIndex, range);
                    directs.RemoveRange(pathIndex - 1, range);
                    return false;
                }
            }
            else if (direction == 2)
            {
                int row = grid1.i1 - 1;
                int col = grid1.i2;
                while ((row >= 0) && (Data[row, col] == -1) && !(row == grid2.i1 && col == grid2.i2))
                {
                    grids.Add(new IntPair(row, col));
                    directs.Add(2);
                    if (turnNum != 0)
                    {
                        if (IsRemoivable_Step(0, turnNum - 1, new IntPair(row, col), grid2, grids, directs))
                        {
                            return true;
                        }
                        else if (IsRemoivable_Step(1, turnNum - 1, new IntPair(row, col), grid2, grids, directs))
                        {
                            return true;
                        }
                    }
                    row--;
                }
                if ((row >= 0) && (row == grid2.i1 && col == grid2.i2))
                {
                    grids.Add(new IntPair(row, col));
                    directs.Add(2);
                    return true;
                }
                else
                {
                    int range = grids.Count - pathIndex;
                    grids.RemoveRange(pathIndex, range);
                    directs.RemoveRange(pathIndex - 1, range);
                    return false;
                }
            }
            else if (direction == 3)
            {
                int row = grid1.i1 + 1;
                int col = grid1.i2;
                while ((row <= MAXROW) && (Data[row, col] == -1) && !(row == grid2.i1 && col == grid2.i2))
                {
                    grids.Add(new IntPair(row, col));
                    directs.Add(3);
                    if (turnNum != 0)
                    {
                        if (IsRemoivable_Step(0, turnNum - 1, new IntPair(row, col), grid2, grids, directs))
                        {
                            return true;
                        }
                        else if (IsRemoivable_Step(1, turnNum - 1, new IntPair(row, col), grid2, grids, directs))
                        {
                            return true;
                        }
                    }
                    row++;
                }
                if ((row <= MAXROW) && (row == grid2.i1 && col == grid2.i2))
                {
                    grids.Add(new IntPair(row, col));
                    directs.Add(3);
                    return true;
                }
                else
                {
                    int range = grids.Count - pathIndex;
                    grids.RemoveRange(pathIndex, range);
                    directs.RemoveRange(pathIndex - 1, range);
                    return false;
                }
            }
            return false;
        }

        private bool FindRemovable(IntPair grid1, IntPair grid2)
        {
            if (Data[grid1.i1, grid1.i2] == -1) return false;

            int imageIndex = Data[grid1.i1, grid1.i2];
            List<IntPair> grids = new List<IntPair>();
            grids.Add(grid1);
            List<int> directs = new List<int>(); 
            
            // find up
            if (FindRemovable_Step(imageIndex, 2, 2, grid1, grid2, grids, directs))
            {
                return true;
            }
            // find down
            if (FindRemovable_Step(imageIndex, 3, 2, grid1, grid2, grids, directs))
            {
                return true;
            }
            // find left
            if (FindRemovable_Step(imageIndex, 0, 2, grid1, grid2, grids, directs))
            {
                return true;
            }
            // find right
            if (FindRemovable_Step(imageIndex, 1, 2, grid1, grid2, grids, directs))
            {
                return true;
            }

            return false;
        }
        /* 
         * direction: 0 - left,  1 - right, 2 - up,  3 - down
         */
        private bool FindRemovable_Step(int index, int direction, int turnNum, 
            IntPair grid1, IntPair grid2, List<IntPair> grids, List<int> directs)
        {
            int pathIndex = grids.Count;

            if (direction == 0)
            {
                int row = grid1.i1;
                int col = grid1.i2 - 1;
                while ((col >= 0) && (Data[row, col] != index) && (Data[row, col] == -1))
                {
                    grids.Add(new IntPair(row, col));
                    directs.Add(0);
                    if (turnNum != 0)
                    {
                        if (FindRemovable_Step(index, 2, turnNum - 1, new IntPair(row, col), grid2, grids, directs))
                        {
                            return true;
                        }
                        else if (FindRemovable_Step(index, 3, turnNum - 1, new IntPair(row, col), grid2, grids, directs))
                        {
                            return true;
                        }
                    }
                    col--;
                }
                if ((col >= 0) && (Data[row, col] == index))
                {
                    grid2.i1 = row;
                    grid2.i2 = col;
                    return true;
                }
                else
                {
                    grids.RemoveRange(pathIndex, grids.Count - pathIndex);
                    directs.RemoveRange(pathIndex-1, grids.Count - pathIndex);
                    return false;
                }
            }
            else if (direction == 1)
            {
                int row = grid1.i1;
                int col = grid1.i2 + 1;
                while ((col <= MAXCOL) && (Data[row, col] != index) && (Data[row, col] == -1))
                {
                    grids.Add(new IntPair(row, col));
                    directs.Add(1);
                    if (turnNum != 0)
                    {
                        if (FindRemovable_Step(index, 2, turnNum - 1, new IntPair(row, col), grid2, grids, directs))
                        {
                            return true;
                        }
                        else if (FindRemovable_Step(index, 3, turnNum - 1, new IntPair(row, col), grid2, grids, directs))
                        {
                            return true;
                        }
                    }
                    col++;
                }
                if ((col <= MAXCOL) && (Data[row, col] == index))
                {
                    grid2.i1 = row;
                    grid2.i2 = col;
                    return true;
                }
                else
                {
                    grids.RemoveRange(pathIndex, grids.Count - pathIndex);
                    directs.RemoveRange(pathIndex - 1, grids.Count - pathIndex);
                    return false;
                }
            }
            else if (direction == 2)
            {
                int row = grid1.i1 - 1;
                int col = grid1.i2;
                while ((row >= 0) && (Data[row, col] != index) && (Data[row, col] == -1))
                {
                    grids.Add(new IntPair(row, col));
                    directs.Add(2);
                    if (turnNum != 0)
                    {
                        if (FindRemovable_Step(index, 0, turnNum - 1, new IntPair(row, col), grid2, grids, directs))
                        {
                            return true;
                        }
                        else if (FindRemovable_Step(index, 1, turnNum - 1, new IntPair(row, col), grid2, grids, directs))
                        {
                            return true;
                        }
                    }
                    row--;
                }
                if ((row >= 0) && (Data[row, col] == index))
                {
                    grid2.i1 = row;
                    grid2.i2 = col;
                    return true;
                }
                else
                {
                    grids.RemoveRange(pathIndex, grids.Count - pathIndex);
                    directs.RemoveRange(pathIndex - 1, grids.Count - pathIndex);
                    return false;
                }
            }
            else if (direction == 3)
            {
                int row = grid1.i1 + 1;
                int col = grid1.i2;
                grids.Add(new IntPair(row, col));
                directs.Add(3);
                while ((row <= MAXROW) && (Data[row, col] != index) && (Data[row, col] == -1))
                {
                    grids.Add(new IntPair(row, col));
                    directs.Add(3);
                    if (turnNum != 0)
                    {
                        if (FindRemovable_Step(index, 0, turnNum - 1, new IntPair(row, col), grid2, grids, directs))
                        {
                            return true;
                        }
                        else if (FindRemovable_Step(index, 1, turnNum - 1, new IntPair(row, col), grid2, grids, directs))
                        {
                            return true;
                        }
                    }
                    row++;
                }
                if ((row <= MAXROW) && (Data[row, col] == index))
                {
                    grid2.i1 = row;
                    grid2.i2 = col;
                    return true;
                }
                else
                {
                    grids.RemoveRange(pathIndex, grids.Count - pathIndex);
                    directs.RemoveRange(pathIndex - 1, grids.Count - pathIndex);
                    return false;
                }
            }
            return false;
        }
    }

    class IntPair
    {
        public int i1=0, i2=0;
        public IntPair(int a1, int a2)
        {
            i1 = a1;
            i2 = a2;
        }
        public void SetValue(int a1, int a2)
        {
            i1 = a1;
            i2 = a2;
        }
        public void SetValue(IntPair a)
        {
            i1 = a.i1;
            i2 = a.i2;
        }
    }
    class GameInfo
    {
        public List<IntPair> Info = new List<IntPair>();
        public int Level = 0;
        public GameInfo(int aLevel)
        {
            Level = aLevel; 
        }
        public int ImageCount()
        {
            int c = 0; 
            for (int i = 0; i < Info.Count; i++)
            {
                c += Info[i].i2;
            }
            return c;
        }
    }
}
